package cn.enjoy.redisson.controller;

import cn.enjoy.redisson.util.RedissonUtil;
import cn.enjoy.tool.api.R;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.redisson.api.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.*;

/**
 * redisson分布式锁
 * https://github.com/redisson/redisson/wiki/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%92%8C%E5%90%8C%E6%AD%A5%E5%99%A8
 *
 * @author K
 * @date 2021/7/1 下午4:38
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("lock")
public class LockController {

    private final RedissonUtil redissonUtil;
    private final RedissonClient redisson;
    Semaphore semaphore = new Semaphore(3);

    /**
     * 可重入锁，阻塞
     * 凡是提供了leaseTime——锁定持续期间，都不会续期，过期后自动删除锁定的key
     * 默认续期
     */
    @GetMapping("lock-renewal")
    public void lockRenewal() throws InterruptedException {
        // 自动续期，默认锁定时间30秒，每10秒检查一次，续期到满30秒（可阅读源码查看）
        RLock lock = redissonUtil.getLock("lock-renewal");
        lock.lock();
        try {
            System.out.println("续期锁——加锁成功！");
            Thread.sleep(11000);
        } finally {
            lock.unlock();
            System.out.println("续期锁——解锁！");
        }
    }

    /**
     * 可重入锁，阻塞
     * 凡是提供了leaseTime——锁定持续期间，都不会续期，过期后自动删除锁定的key
     * 不续期
     */
    @GetMapping("lock-lease-time")
    public void lockLeaseTime() throws InterruptedException {
        // 不自动续期，到期后锁定的key自动删除。unlock时会抛异常，提示不是当前线程获取的锁，解锁失败。
        RLock lock = redissonUtil.lock("lock", 5, TimeUnit.SECONDS);
        lock.lock();
        try {
            System.out.println("不续期锁——加锁成功！");
            Thread.sleep(6000);
        } finally {
            System.out.println("不续期锁——解锁！");
            lock.unlock();
//            redissonUtil.unlock(lock);
        }
    }

    /**
     * 可重入锁，waitTime期间不断获取锁，超过waitTime时间后就返回true或false
     * 凡是提供了leaseTime——锁定持续期间，都不会续期，过期后自动删除锁定的key
     */
    @GetMapping("try-lock")
    public void tryLock() throws InterruptedException {

        try {
            if (redissonUtil.tryLock("tryLock", 5, 10)) {
                System.out.println("try-lock 加锁成功！");
            } else {
                System.out.println("try-lock 加锁失败！");
            }
        } finally {
            redissonUtil.unlock("tryLock");
            System.out.println("try-lock 解锁！");
        }
    }

    @GetMapping("semaphore-delete")
    public void semaphoreDelete() {
        String semaphoreName = "semaphore";
        RSemaphore semaphore = redisson.getSemaphore(semaphoreName);
        System.out.println("delete 信号量 permits数" + semaphore.availablePermits());
        semaphore.drainPermits();
        semaphore.delete();
    }

    @GetMapping("semaphore-release")
    public void semaphoreRelease() {
        String semaphoreName = "semaphore";
        semaphoreRelease(semaphoreName);
    }

    /**
     * 信号量
     * 可用于限流、停车
     */
    @GetMapping("semaphore")
    public void semaphore() {
        try {
            String semaphoreName = "semaphore";
            RSemaphore semaphore = redisson.getSemaphore(semaphoreName);
            if (!semaphore.isExists()) {
                // 设置许可（信号量）
                semaphore.trySetPermits(5);
            }
            System.out.println("create 信号量 permits数" + semaphore.availablePermits());
            ExecutorService executorService = Executors.newFixedThreadPool(5);
            executorService.execute(() -> semaphoreAcquire(semaphoreName));
            executorService.execute(() -> semaphoreAcquire(semaphoreName));
            executorService.execute(() -> semaphoreAcquire(semaphoreName));
            executorService.shutdown();

            while (true) {
                if (executorService.isTerminated()) {
                    System.out.println("关闭线程池");
                    System.out.println("关闭线程池后 信号量permits数" + semaphore.availablePermits());
                    // semaphore.drainPermits()获取并归还所有可用permits，与availablePermits()类似
                    // 最后必须删除
//                    semaphore.delete();
//                    System.out.println("删除后 信号量permits数" + semaphore.availablePermits());
                    break;
                } else {
                    Thread.sleep(2000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @GetMapping("semaphore-java-release")
    public void semaphoreJavaRelease() {
        semaphoreReleaseJava(semaphore);
    }

    @GetMapping("semaphore-java")
    public void semaphoreJava() {
        try {
            System.out.println("create Java 信号量 permits数" + semaphore.availablePermits());
            ExecutorService executorService = Executors.newFixedThreadPool(8);
            executorService.execute(() -> semaphoreAcquireJava(semaphore));
            executorService.execute(() -> semaphoreAcquireJava(semaphore));
            executorService.execute(() -> semaphoreAcquireJava(semaphore));
            // 有一个线程会被阻塞，等待信号量释放，只会打印3条获取信号的记录。
            // 因为还有任务在执行，线程池不会销毁
            executorService.execute(() -> semaphoreAcquireJava(semaphore));
            executorService.shutdown();

            while (true) {
                if (executorService.isTerminated()) {
                    System.out.println("关闭线程池");
                    System.out.println("关闭线程池后 Java 信号量permits数" + semaphore.availablePermits());
                    // semaphore.drainPermits()获取并归还所有可用permits，与availablePermits()类似

                    break;
                } else {
                    Thread.sleep(2000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @GetMapping("semaphore-java-CyclicBarrier")
    public R semaphoreJavaCyclicBarrier() {
        try {
            System.out.println("create Java 信号量 permits数" + semaphore.availablePermits());
            ExecutorService executorService = Executors.newFixedThreadPool(8);
            CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
            executorService.execute(() -> semaphoreAcquireJava(cyclicBarrier, semaphore));
            executorService.execute(() -> semaphoreAcquireJava(cyclicBarrier, semaphore));
            executorService.execute(() -> semaphoreAcquireJava(cyclicBarrier, semaphore));

            // 有一个线程会被阻塞，等待信号量释放，只会打印3条获取信号的记录
            // 同时还需要再提交2个线程任务，触发CyclicBarrier通知所有线程一起获取信号量，不然信号量释放了，这个线程仍会处于wait
            executorService.execute(() -> semaphoreAcquireJava(cyclicBarrier, semaphore));

            System.out.println("关闭线程池");
            System.out.println("关闭线程池后 Java 信号量permits数" + semaphore.availablePermits());

        } catch (Exception e) {
            e.printStackTrace();
        }

        return R.status(true);
    }

    private void semaphoreAcquire(String semaphoreName) {
        RSemaphore semaphore = redisson.getSemaphore(semaphoreName);
        try {
            semaphore.acquire();
            // 建议使用try，如果使用acquire，获取不到会一直阻塞
//            semaphore.tryAcquire(5, TimeUnit.SECONDS);
            System.out.println(Thread.currentThread().getName() + "\tacquire 信号量permits数" + semaphore.availablePermits());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void semaphoreRelease(String semaphoreName) {
        RSemaphore semaphore = redisson.getSemaphore(semaphoreName);
        semaphore.release();
        System.out.println(Thread.currentThread().getName() + "\trelease 信号量permits数" + semaphore.availablePermits());
    }

    private void semaphoreAcquireJava(Semaphore semaphore) {
        try {
            System.out.println(Thread.currentThread().getName());
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + "\tacquire Java 信号量permits数" + semaphore.availablePermits());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void semaphoreReleaseJava(Semaphore semaphore) {
        try {
            System.out.println(Thread.currentThread().getName());
            semaphore.release();
            System.out.println(Thread.currentThread().getName() + "\trelease Java 信号量permits数" + semaphore.availablePermits());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void semaphoreAcquireJava(CyclicBarrier cyclicBarrier, Semaphore semaphore) {
        try {
            System.out.println(Thread.currentThread().getName());
            cyclicBarrier.await();
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + "\tacquire Java 信号量permits数" + semaphore.availablePermits());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 闭锁（锁计数器，等锁准备好以后才放行，与java提供的countDownLatch一样）
     * 百米赛跑，9人参赛，运动员每到终点，颁发一个奖牌（顺序的）。CyclicBarrier是所有运动员到达终点后才颁发奖牌
     * 放学锁门，需等所有班级离开才锁门
     */
    @GetMapping("countDownLatch")
    public void countDownLatch() {
        RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
        latch.trySetCount(1);

        // 在其他线程或其他JVM里
        RCountDownLatch latch2 = redisson.getCountDownLatch("anyCountDownLatch");
        latch2.countDown();

        try {
            latch.await();
            System.out.println("最后锁门 / 颁发完奖牌");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            latch.delete();
        }
    }

    /**
     * 红锁，需要配置redis独立节点
     */
//    @GetMapping("rLock")
//    public void rLock() {
//        RedissonRedLock redLock = redissonUtil.rLock("mm");
//
//        try {
//            boolean b = redLock.tryLock(5, TimeUnit.SECONDS);
//            if (b) {
//                System.out.println("redLock 加锁成功！");
//            } else {
//                System.out.println("redLock 加锁失败！");
//            }
//        } catch (InterruptedException e) {
//            System.out.println("redLock 加锁失败！");
//        } finally {
//            redLock.unlock();
//            System.out.println("解锁");
//        }
//    }

    /**
     * redisson 操作缓存对象
     */
    @PostMapping("bucket")
    public void bucket(String value) throws InterruptedException {
        redissonUtil.set("kk", value);
        System.out.println((String) redissonUtil.get("kk"));

        redissonUtil.expire("kk", 1, TimeUnit.SECONDS);
        Thread.sleep(1000);
        System.out.println((String) redissonUtil.get("kk"));

        redissonUtil.delete("kk");
        System.out.println((String) redissonUtil.get("kk"));
    }

    /**
     * 分布式对象
     * https://github.com/redisson/redisson/wiki/6.-%E5%88%86%E5%B8%83%E5%BC%8F%E5%AF%B9%E8%B1%A1
     */
    @GetMapping("distributed-object")
    public void distributedObject() {
        // map
        RedissonClient redissonClient = redisson;
        RMap<Object, Object> map = redissonClient.getMap("mymap");
        String name = map.getName();

        RKeys keys = redissonClient.getKeys();
        Iterable<String> allKeys = keys.getKeys();
        long numOfDeletedKeys = keys.delete("obj1", "obj2", "obj3");
        long deletedKeysAmount = keys.deleteByPattern("test?");
        String randomKey = keys.randomKey();
        long keysAmount = keys.count();

        // bucket 可以存放任何类型对象
        RBucket<Object> bucket = redissonClient.getBucket("anyObject");
        bucket.set(1);
        Object obj = bucket.get();

        bucket.trySet(2);
        bucket.compareAndSet(3, 5);
        bucket.getAndSet(6);
    }

}
